#! /usr/bin/env python

"""
Response Force Controller

"""

__author__ = 'Florian Krause <f.krause@donders.ru.nl>, \
             Oliver Lindemann <o.lindemann@donders.ru.nl>'
__date__ = 'Wed 31 Mar 2010 03:57:38 PM CEST'


from Tkinter import *
import serial
import sys
import os
import time
import types


class Clock(object) :
    """A class implementing a clock"""

    def __init__ (self):
        """Initialize a clock object"""

        if sys.platform == 'win32':
            self.__cpu_time = time.clock
        else:
            self.__cpu_time = time.time
        self.__init_time = self.__cpu_time()
        self.reset_stopwatch()

    @property
    def time(self):
        """Current time in milliseconds since clock init"""

        return int((self.__cpu_time() - self.__init_time) * 1000)

    @property
    def stopwatch_time(self):
        """Time in milliseconds since last reset_stopwatch"""

        return int((self.__cpu_time() - self.__start) * 1000)

    def reset_stopwatch(self):
        """"Reset the stopwatch """

        self.__start = self.__cpu_time()


class ResponseForceController:
    """A class implementing a Response Force Controller"""

    def __init__(self):
        """Initialize a Response Force Controller object"""

        self.lower_threshold = 10
        self.upper_threshold = 90
        self.serial_port_rfdevice = '-'
        self.serial_port_input_output = '-'
        self.delimiter = ","
        self.clock = Clock()
        self.logfile_name = "Subject00.txt"
        self._baseline1 = 0
        self._baseline2 = 0
        self._rfdata_input = None
        self._mean_buffer_size = 10
        self._mean_btn1 = 0
        self._mean_btn2 = 0
        self._state1 = 0
        self._state2 = 0
        self._buffer = [[], []]
        self.documentation = """
Response Force Controller Documentation
=======================================

Requirements:
    Python 2.6, Tkinter, PySerial and Win32All (Windows only).

Notes:
    When using a USB-to-Serial converter on Linux, create a new serial device
    and link it to the USB device (e.g. "ln /dev/ttyUSB1 /dev/ttyS7").


Initialization:
---------------

Before a recording can be started, the response force device has to be initialized by clicking "File --> Initialize" in the main menu.

Two serial ports can be selected.
One for getting the data from the response force device ("RF Device Port") and another optional one for communicating with another computer for synchronization with the experimental timing ("Communication Port").

The "Communication Port" can receive as well as send data defined by the following protocol:

    Input codes:  0-200   = User defined markers
                  201-255 = Commands

                  Commands: 201 = Set Baseline (see below)

                  (All input codes will be mirrored/sent back!)

    Output codes: 1X = State X for Button 1
                  2X = State X for Button 2

                  State: 0 = no press
                         1 = soft press
                         2 = hard press

Additionally, a filename for logging all incoming changes (from both ports) can be selected.
The format of the logfile is as follows:

    Time,Event,Value1,Value2,State1,State2

    Event: 0 = data         (Value1, Value2 = force data of the two buttons)
           1 = baseline set (Value1, Value2 = new baseline of the two buttons,
                             State1, State2 = 0)
           2 = serial input (Value1 = input value, i.e. marker or command,
                             State1, State2 = 0)


Settings:
---------

The lower as well as the upper threshold (for detecting soft and hard presses) can be set by clicking "Edit --> Settings" in the main menu.
This will also adapt the graphical threshold bars and the colouring of the values shown in the main window for direct visual feedback.

Furthermore, the buffer size to calculate the mean values can be set. This does also reflect the update rate of the GUI.


Record:
-------

By pressing the "Start" button in the main windows, a recording will be started.
Pressing the "Stop" button will stop a recording (pressing the "Start" button again will contiune the recording).

The "Set Baseline" button will set the resting state of each button by an estimation over the average of the last 10 values.
(Thus, both buttons should be in unpressed when setting the baseline."""


    def create_logfile(self):
        """ Create the logfile and write header"""

        self._logfile = open(self.logfile_name, 'w')
        self._logfile.write("#")
        self._logfile.write("Time")
        self._logfile.write(self.delimiter)
        self._logfile.write("Event")
        self._logfile.write(self.delimiter)
        self._logfile.write("Value1")
        self._logfile.write(self.delimiter)
        self._logfile.write("Value2")
        self._logfile.write(self.delimiter)
        self._logfile.write("State1")
        self._logfile.write(self.delimiter)
        self._logfile.write("State2")
        self._logfile.write("\n")

    def connect(self):
        """
        Connect to the response buttons and to another computer

        The initialize message and values from the response buttons are
        written to the logfile.

        """

        try:
            self._rfdata_input = serial.Serial(self.serial_port_rfdevice,
                                               115200, bytesize=8, parity='N',
                                               stopbits=1, timeout=0, xonxoff=0,
                                               rtscts=0)
            if not self._rfdata_input.isOpen():
                raise Exception
        except:
            return - 1

        if self.serial_port_input_output != '-':
            try:
                self._input_output = serial.Serial(self.serial_port_input_output,
                                                   115200, bytesize=8, parity='N',
                                                   stopbits=1, timeout=0,
                                                   xonxoff=0, rtscts=0)
                if not self._input_output.isOpen():
                    raise Exception
            except:
                return - 2

        cnt = 0
        while cnt < 10:
            data, inout_data, rfdata_time = self.read_ports(timeout=4000)
            if not data:
                return - 3
            self._logfile.write("#" + data + "\n")
            cnt += 1

        return 1

    def close_connections(self):
        """Close the serial ports"""
        try:
            self._rfdata_input.close()
            self._input_output.close()
        except:
            pass

    def detect_threshold(self, mean, last_state):
        """
        Detect if one of the thresholds is reached an signal a change

        Keyword arguments:
        mean       -- the mean value from the response button
        last_state -- the state from the last detection

        """

        if mean <= self.lower_threshold:
            status = 0
        elif mean >= self.upper_threshold:
            status = 2
        else:
            status = 1

        if last_state == 0 and status == 1:
            return status, 1
        elif last_state > 0 and status == 0:
            return status, 1
        elif last_state == 2 and status == 1:
            return status, 1
        elif last_state == 1 and status == 2:
            return status, 1
        return status, 0


    def read_ports(self, timeout=0):
        """
        Read out both serial ports

        Keyword arguments:
        timeout -- try to read for given amount of time (default=0)

        """

        data_rfdevice = ""
        data_input_output = ""
        data_rfdevice_time = 0
        timeout_time = self.clock.time + timeout
        while True:

            if self.serial_port_input_output != '-':
                data_input_output = self._input_output.read()
                if data_input_output:
                    data_input_output_time = self.clock.time
                    # Mirror
                    self._input_output.write(data_input_output)
                    # Log immediately
                    self._logfile.write(repr(data_input_output_time))
                    self._logfile.write(self.delimiter)
                    self._logfile.write("2")
                    self._logfile.write(self.delimiter)
                    self._logfile.write(repr(ord(data_input_output)))
                    self._logfile.write(self.delimiter)
                    self._logfile.write("0")
                    self._logfile.write(self.delimiter)
                    self._logfile.write("0")
                    self._logfile.write(self.delimiter)
                    self._logfile.write("0")
                    self._logfile.write("\n")

            byte = self._rfdata_input.read()
            if byte:
                if byte != '\n' and byte != '\r':
                    data_rfdevice = data_rfdevice + byte
                elif byte == '\r':
                    data_rfdevice_time = self.clock.time
                    break

            if not data_rfdevice and timeout and self.clock.time >= timeout_time:
                data_rfdevice_time = None
                break

        return data_rfdevice, data_input_output, data_rfdevice_time

    def port_wait(self, char):
        """
        Wait for certain input

        Keyword arguments:
        char -- the character to be waited for

        """

        while True:
            byte = self._rfdata_input.read()
            if byte == char:
                break

    def empty_rf_buffer(self):
        """Empty the response force device's buffer"""

        self._logfile.write("# Emptying ")
        cnt = 0
        while True:
            byte = self._rfdata_input.read()
            cnt += 1
            if byte == "":
                break
        self._logfile.write("{0} bytes\n".format(cnt))

    def process_data(self):
        """Proecess the incoming data"""

        data, inout_data, rfdata_time = self.read_ports()
        try:
            button1, button2 = (int(x[2:]) for x in data.split(","))
        except:
            self._logfile.write("########## Could not interpret: " + data + "\n")
            return time

        # Buffering
        if len(self._buffer[0]) >= self._mean_buffer_size:
            del self._buffer[0][0]
            del self._buffer[1][0]
        self._buffer[0].append(button1 - self._baseline1)
        self._buffer[1].append(button2 - self._baseline2)

        # Calc mean and status
        self._mean_btn1 = sum(self._buffer[0]) // len(self._buffer[0])
        self._mean_btn2 = sum(self._buffer[1]) // len(self._buffer[1])
        self._state1, change1 = self.detect_threshold(self._mean_btn1, self._state1)
        self._state2, change2 = self.detect_threshold(self._mean_btn2, self._state2)

        if self.serial_port_input_output != '-':
            if change1:
                self._input_output.write(chr(10 + self._state1)) #10 + (0,1,2)
            if change2:
                self._input_output.write(chr(20 + self._state2)) #20 + (0,1,2)

        # Logging rf data
        self._logfile.write(repr(rfdata_time))
        self._logfile.write(self.delimiter)
        self._logfile.write("0")
        self._logfile.write(self.delimiter)
        self._logfile.write(repr(self._buffer[0][-1]))
        self._logfile.write(self.delimiter)
        self._logfile.write(repr(self._buffer[1][-1]))
        self._logfile.write(self.delimiter)
        self._logfile.write(repr(self._state1))
        self._logfile.write(self.delimiter)
        self._logfile.write(repr(self._state2))
        self._logfile.write("\n")

        if len(self._buffer[0]) == self._mean_buffer_size - 1:
                self.set_baseline()

        # Process commands
        if inout_data:
            if ord(inout_data) == 201:
                self.set_baseline()

        return time

    def set_baseline(self):
        """
        Set the baseline

        A new baseline will be calculated based on the current mean value.

        """
        baseline_time = self.clock.time
        self._baseline1 = self._mean_btn1 + self._baseline1
        self._baseline2 = self._mean_btn2 + self._baseline2
        self._logfile.write(repr(baseline_time))
        self._logfile.write(self.delimiter)
        self._logfile.write("1")
        self._logfile.write(self.delimiter)
        self._logfile.write(repr(self._baseline1))
        self._logfile.write(self.delimiter)
        self._logfile.write(repr(self._baseline2))
        self._logfile.write(self.delimiter)
        self._logfile.write("0")
        self._logfile.write(self.delimiter)
        self._logfile.write("0")
        self._logfile.write("\n")

    def quit(self):
        """Quit the application"""

        self.close_connections()
        self._logfile.close()


class ResponseForceControllerGUI(Frame, ResponseForceController):
    """A class implementing a GUI for the response force controller"""

    def __init__(self, master=None):
        """
        Initialize a GUI object

        Keyword arguments:
        master -- the name of the master widget

        """

        ResponseForceController.__init__(self)
        Frame.__init__(self, master,
                            width=800, height=600, pady=20)
        self.pack_propagate(0)

        # Set keybingings
        self.master.bind("<Control-i>", lambda e: self.init_dialogue())
        self.master.bind("<Control-q>", lambda e: self.quit_gui())
        self.master.bind("<Control-s>", lambda e: self.settings_dialogue())
        self.master.bind("<F1>", lambda e: self.help())
        self.pack()

        # Create menubar
        self.menubar = Menu(self.master)
        self.filemenu = Menu(self.menubar, tearoff=0)
        self.filemenu.add_command(label="Initialize", underline=0,
                             command=self.init_dialogue, accelerator="Ctrl+I")
        self.filemenu.add_command(label="Quit", underline=0,
                                  command=self.quit_gui,
                             accelerator="Ctrl+Q")
        self.menubar.add_cascade(label="File", underline=0, menu=self.filemenu)
        self.editmenu = Menu(self.menubar, tearoff=0)
        self.editmenu.add_command(label="Settings", underline=0,
                             command=self.settings_dialogue, accelerator="Ctrl+S")
        self.menubar.add_cascade(label="Edit", underline=0, menu=self.editmenu)
        self.helpmenu = Menu(self.menubar, tearoff=0)
        self.helpmenu.add_command(label="Contents", underline=0, command=self.help,
                             accelerator="F1")
        self.helpmenu.add_command(label="About", underline=0, command=self.about)
        self.menubar.add_cascade(label="Help", underline=0, menu=self.helpmenu)
        self.master.config(menu=self.menubar)

        self.labelframe = Frame(self, width=800)
        self.labelframe.pack()
        self.labelframe1 = Frame(self.labelframe, width=200, height=50)
        self.labelframe1.pack_propagate(0)
        self.labelframe1.pack(side=LEFT, fill=X)
        self.labelframemiddle = Frame(self.labelframe, width=400, height=50)
        self.labelframemiddle.pack_propagate(0)
        self.labelframemiddle.pack(side=LEFT, fill=X)
        self.labelframe2 = Frame(self.labelframe, width=200, height=50)
        self.labelframe2.pack_propagate(0)
        self.labelframe2.pack(side=LEFT, fill=X)
        self._outtext1 = StringVar()
        self._outtextmiddle = StringVar()
        self._outtext2 = StringVar()
        self._outtext1.set("-")
        self._outtextmiddle.set("Welcome!")
        self._outtext2.set("-")
        self.label1 = Label(self.labelframe1, textvariable=self._outtext1,
                            bg="white", anchor=CENTER, font=("Arial", "25"),
                            width=3, bd=2, relief=SUNKEN)
        self.label1.pack()
        self.labelmiddle = Label(self.labelframemiddle,
                                 textvariable=self._outtextmiddle,
                                 anchor=CENTER, font=("Arial", "25"))
        self.labelmiddle.pack()
        self.label2 = Label(self.labelframe2, textvariable=self._outtext2,
                            bg="white", anchor=CENTER, font=("Arial", "25"),
                            width=3, bd=2, relief=SUNKEN)
        self.label2.pack()

        self.canvasframe = Frame(self, width=800, height=500)
        self.canvasframe.pack(fill=X)
        self.canvasframe1 = Frame(self.canvasframe, width=200, height=500)
        self.canvasframe1.pack_propagate(0)
        self.canvasframe1.pack(side=LEFT, fill=X)
        self.canvasframemiddle = Frame(self.canvasframe, width=400, height=500,
                                      pady=25)
        self.canvasframemiddle.pack_propagate(0)
        self.canvasframemiddle.pack(side=LEFT, fill=X)
        self.canvasframe2 = Frame(self.canvasframe, width=200, height=500)
        self.canvasframe2.pack_propagate(0)
        self.canvasframe2.pack(side=LEFT, fill=X)
        self.canvas1 = Canvas(self.canvasframe1, width=200, height=500)
        self.canvas1.pack()

        self.startstopframe = Frame(self.canvasframemiddle)
        self.startstopframe.pack(fill=Y)
        self.startbutton = Button(self.startstopframe, text="Start",
                                  font=("Arial", "16"), bd=2, relief=RAISED,
                                  state=DISABLED, command=self.start_recording)
        self.startbutton.pack(side=LEFT)
        self.stopbutton = Button(self.startstopframe, text="Stop",
                                 font=("Arial", "16"), bd=2, relief=RAISED,
                                 state=DISABLED, command=self.stop_recording)
        self.stopbutton.pack(side=LEFT)
        self.baselinebutton = Button(self.canvasframemiddle, text="Set Baseline",
                                     font=("Arial", "16"), bd=2, relief=RAISED,
                                     state=DISABLED, command=self.set_baseline)
        self.baselinebutton.pack(side=BOTTOM)
        self.canvas2 = Canvas(self.canvasframe2, width=200, height=500)
        self.canvas2.pack()

        self.draw_canvas()

    def draw_canvas(self):
        """Draw the canvas"""

        self.canvas1.delete(ALL)
        self.canvas2.delete(ALL)
        self.line1 = self.canvas1.create_line(100, 470, 100, 470, width=30, fill="blue")
        self.canvas1.create_line(0, 471, 200, 471, width=2, fill="black")
        self.canvas1.create_line(0, 471 - self.lower_threshold * 3, 75,
                                 471 - self.lower_threshold * 3, width=2,
                                 fill="orange")
        self.canvas1.create_line(125, 471 - self.lower_threshold * 3, 200,
                                 471 - self.lower_threshold * 3, width=2,
                                 fill="orange")

        self.canvas1.create_line(0, 471 - self.upper_threshold * 3, 75,
                                 471 - self.upper_threshold * 3, width=2,
                                 fill="red")
        self.canvas1.create_line(125, 471 - self.upper_threshold * 3, 200,
                                 471 - self.upper_threshold * 3, width=2,
                                 fill="red")

        self.line2 = self.canvas2.create_line(100, 470, 100, 470, width=30, fill="blue")
        self.canvas2.create_line(0, 471, 200, 471, width=2, fill="black")
        self.canvas2.create_line(0, 471 - self.lower_threshold * 3, 75,
                                 471 - self.lower_threshold * 3, width=2,
                                 fill="orange")
        self.canvas2.create_line(125, 471 - self.lower_threshold * 3, 200,
                                 471 - self.lower_threshold * 3, width=2,
                                 fill="orange")
        self.canvas2.create_line(0, 471 - self.upper_threshold * 3, 75,
                                 471 - self.upper_threshold * 3, width=2,
                                 fill="red")
        self.canvas2.create_line(125, 471 - self.upper_threshold * 3, 200,
                                 471 - self.upper_threshold * 3, width=2,
                                 fill="red")

    def init(self):
        """Initialize the response force buttons"""

        self.labelmiddle.config(fg="yellow")
        self._outtextmiddle.set("Connecting...")
        self.update()
        self.create_logfile()
        success = self.connect()
        if success == 1:
            self.labelmiddle.config(fg="green")
            self._outtextmiddle.set("Initialized (Ready)")
            self.startbutton.config(bg="green", activebackground="#777fff777",
                                   state=NORMAL)
            self.filemenu.entryconfig(0, state=DISABLED)
            self.master.unbind("<Control-i>")

        else:
            self.close_connections()
            self._logfile.close()
            os.remove(self.logfile_name)
            self.labelmiddle.config(fg="red")
            if success == -1:
                self._outtextmiddle.set("RF Device Port Error")
            if success == -2:
                self._outtextmiddle.set("Communication Port Error")
            if success == -3:
                self._outtextmiddle.set("Timeout")
        self.update()

    def start_recording(self):
        """Start a recording"""

        # Empty buffers
        self.empty_rf_buffer
        self._rfdevice_input.flushInput()
        self._input_output.flushInput()
        self._input_output.flushOutput()

        self._recording = True
        self.labelmiddle.config(fg="yellow")
        self._outtextmiddle.set("Running...")
        self.startbutton.config(relief=SUNKEN, state=DISABLED)
        self.stopbutton.config(state=NORMAL, bg="red", activebackground="#fff777777")
        self.baselinebutton.config(state=NORMAL)

        cnt = 0
        while self._recording:

            # Get the data
            time = self.process_data()

            # Update GUI
            cnt += 1
            if cnt > self._mean_buffer_size:

                if self._state1 == 0:
                    self.label1.config(fg="green")
                elif self._state1 == 1:
                    self.label1.config(fg="orange")
                elif self._state1 == 2:
                    self.label1.config(fg="red")

                if self._state2 == 0:
                    self.label2.config(fg="green")
                elif self._state2 == 1:
                    self.label2.config(fg="orange")
                elif self._state2 == 2:
                    self.label2.config(fg="red")

                self.canvas1.delete(self.line1)
                self.line1 = self.canvas1.create_line(100, 470 - self._mean_btn1 * 3,
                                                      100, 470, width=30, fill="blue")

                self.canvas2.delete(self.line2)
                self.line2 = self.canvas2.create_line(100, 470 - self._mean_btn2 * 3,
                                                      100, 470, width=30, fill="blue")

                self._outtext1.set(repr(self._mean_btn1))
                self._outtext2.set(repr(self._mean_btn2))
                self.update()
                cnt = 0

    def stop_recording(self):
        """Stop a recording"""

        self._recording = False
        self.label1.config(fg="black")
        self.labelmiddle.config(fg="green")
        self.label2.config(fg="black")
        self._outtext1.set("-")
        self._outtextmiddle.set("Stopped (Ready)")
        self._outtext2.set("-")
        self.startbutton.config(relief=RAISED, state=NORMAL)
        self.stopbutton.config(bg=self.baselinebutton.cget("bg"),
                               activebackground=self.baselinebutton.cget("activebackground"),
                               state=DISABLED)
        self.baselinebutton.config(state=DISABLED)

    def init_dialogue(self):
        """Show the initialize diaogue window"""

        self.initdialogue = Toplevel(self.master, padx=5, pady=5)
        self.initdialogue.title("Initialize")
        self.initdialogue.transient(self.master)
        self.initdialogue.grab_set()
        self.initdialogue.focus_set()
        self.initdialogue.bind("<Escape>", lambda e: self.initdialogue.destroy())
        self.initdialogue.bind("<Return>", lambda e: self.init_apply())

        self.portframe = LabelFrame(self.initdialogue, text="Connection")
        self.portframe.grid(row=0, column=0, padx=5, pady=5, ipadx=5, ipady=5)

        if os.name == 'posix':
            import glob
            usbports = ['-']
            for port in glob.glob('/dev/ttyUSB*'):
                usbports.append(port)
            usbports.sort()
            self.ports = []
            for port in usbports:
                self.ports.append(port)
                serialports = glob.glob('/dev/ttyS*')
            for port in serialports:
                self.ports.append(port)

        else:
            self.ports = ['-']
            for i in range(256):
                try:
                    s = serial.Serial(i)
                    if s.isOpen():
                        self.ports.append(s.portstr)
                        s.close()
                except serial.SerialException:
                    pass
        self.ports.sort()

        self.inlabel = Label(self.portframe, text="Serial Port RF Device:")
        self.inlabel.grid(row=0, column=0, sticky=W)
        self.invariable = StringVar(self.portframe)

        if os.name == "posix":
            if len(usbports) > 1:
                self.invariable.set(usbports[1])
            else:
                self.invariable.set(self.serial_port_rfdevice)
            self.inentry = OptionMenu(self.portframe, self.invariable, *usbports)

        else:
            if len(self.ports) > 1:
                self.invariable.set(self.ports[1])
            else:
                self.invariable.set(self.serial_port_rfdevice)
            self.inentry = OptionMenu(self.portframe, self.invariable, *self.ports)
        self.inentry.config(takefocus=1)
        self.inentry.grid(row=0, column=1, sticky=W)
        self.outlabel = Label(self.portframe, text="Serial Port Communication:")
        self.outlabel.grid(row=1, column=0, sticky=W)
        self.outvariable = StringVar(self.portframe)

        self.outvariable.set(self.serial_port_input_output)
        self.outentry = OptionMenu(self.portframe, self.outvariable, *self.ports)
        self.outentry.config(takefocus=1)
        self.outentry.grid(row=1, column=1, sticky=W)

        self.logfile_nameframe = LabelFrame(self.initdialogue, text="Logfile")
        self.logfile_nameframe.grid(row=1, column=0, padx=5, pady=5, ipadx=5, ipady=5)

        self.filelabel = Label(self.logfile_nameframe, text="Filename:")
        self.filelabel.grid(row=0, column=0, sticky=W)
        self.fileentry = Entry(self.logfile_nameframe, width=15)
        self.fileentry.insert(0, self.logfile_name)
        self.fileentry.grid(row=0, column=1, sticky=W)

        self.initokaybutton = Button(self.initdialogue, text="Okay",
                                     takefocus=FALSE, command=self.init_apply)
        self.initokaybutton.grid(row=5, columnspan=5)

    def init_apply(self):
        """Apply settings from the initialize window"""

        self.logfile_name = self.fileentry.get()
        try:
            open(self.logfile_name)
            self.fileentry.config(bg="red")
            self.fileentry.focus_set()
        except:
            self.serial_port_rfdevice = self.invariable.get()
            self.serial_port_input_output = self.outvariable.get()
            self.initdialogue.destroy()
            self.init()

    def settings_dialogue(self):
        """Show the settings dialogue window"""

        self.settingsdialogue = Toplevel(self.master, padx=5, pady=5)
        self.settingsdialogue.title("Settings")
        self.settingsdialogue.transient(self.master)
        self.settingsdialogue.grab_set()
        self.settingsdialogue.focus_set()
        self.settingsdialogue.bind("<Escape>", lambda e:
                                   self.settingsdialogue.destroy())
        self.settingsdialogue.bind("<Return>", lambda e: self.settings_apply())

        self.thresholdframe = LabelFrame(self.settingsdialogue, text="Thresholds")
        self.thresholdframe.grid(row=0, column=0, padx=5, pady=5, ipadx=5, ipady=5)

        self.lowerthresholdlabel = Label(self.thresholdframe, text="Lower Threshold:")
        self.lowerthresholdlabel.grid(row=0, column=0, sticky=W)
        self.lowerthresholdentry = Entry(self.thresholdframe, width=3)
        self.lowerthresholdentry.insert(0, self.lower_threshold)
        self.lowerthresholdentry.grid(row=0, column=1, sticky=W)
        self.upperthresholdlabel = Label(self.thresholdframe, text="Upper Threshold:")
        self.upperthresholdlabel.grid(row=1, column=0, sticky=W)
        self.upperthresholdentry = Entry(self.thresholdframe, width=3)
        self.upperthresholdentry.insert(0, self.upper_threshold)
        self.upperthresholdentry.grid(row=1, column=1, sticky=W)

        self.bufferframe = LabelFrame(self.settingsdialogue, text="Buffering")
        self.bufferframe.grid(row=1, column=0, padx=5, pady=5, ipadx=5, ipady=5)
        self.meanbuffersizelabel = Label(self.bufferframe, text="Mean Buffer Size:")
        self.meanbuffersizelabel.grid(row=0, column=0, sticky=W)
        self.meanbuffersizeentry = Entry(self.bufferframe, width=3)
        self.meanbuffersizeentry.insert(0, self._mean_buffer_size)
        self.meanbuffersizeentry.grid(row=0, column=1, sticky=W)

        self.settingsokaybutton = Button(self.settingsdialogue, text="Okay",
                                         takefocus=FALSE,
                                         command=self.settings_apply)
        self.settingsokaybutton.grid(row=2, columnspan=2)

    def settings_apply(self):
        """Apply the settings from the settings dialogue window"""

        validated = True
        try:
            lower_threshold = int(self.lowerthresholdentry.get())
            if not 0 <= lower_threshold <= 150:
                raise Exception
            self.lower_threshold = lower_threshold
            self.lowerthresholdentry.config(bg="white")
        except:
            self.lowerthresholdentry.config(bg="red")
            self.lowerthresholdentry.focus_set()
            validated = False
        try:
            upper_threshold = int(self.upperthresholdentry.get())
            if not 0 <= upper_threshold <= 150:
                raise Exception
            self.upper_threshold = upper_threshold
            self.upperthresholdentry.config(bg="white")
        except:
            self.upperthresholdentry.config(bg="red")
            self.upperthresholdentry.focus_set()
            validated = False
        try:
            mean_buffersize = int(self.meanbuffersizeentry.get())
            if not 0 <= mean_buffersize:
                raise Exception
            self._mean_buffer_size = mean_buffersize
            self.meanbuffersizeentry.config(bg="white")
        except:
            self.meanbuffersizeentry.config(bg="red")
            self.meanbuffersizeentry.focus_set()
            validated = False
        self.settingsdialogue.update()
        if validated == True:
            self.draw_canvas()
            self.settingsdialogue.destroy()

    def about(self):
        """Show the about dialogue window"""

        self.aboutdialogue = Toplevel(self.master, padx=5, pady=5)
        self.aboutdialogue.title("About")
        self.aboutdialogue.transient(self.master)
        self.aboutdialogue.grab_set()
        self.aboutdialogue.focus_set()
        self.aboutdialogue.bind("<Button-1>", lambda e:
                                self.aboutdialogue.destroy())
        self.aboutdialogue.bind("<Escape>", lambda e:
                                self.aboutdialogue.destroy())
        self.aboutdialogue.bind("<Return>", lambda e:
                                self.aboutdialogue.destroy())

        self.aboutlabel1 = Label(self.aboutdialogue,
                                 text="Responce Force Monitor",
                                 font=("Arial", "14", "bold"))
        self.aboutlabel1.pack()
        self.aboutlabel2 = Label(self.aboutdialogue,
                                 text="{0}".format(__date__),
                                 font=("Arial", "7", "italic"))
        self.aboutlabel2.pack()
        self.aboutlabel3 = Label(self.aboutdialogue,
                                 text="",
                                 font=("Arial", "10"))
        self.aboutlabel3.pack()
        self.aboutlabel4 = Label(self.aboutdialogue,
                                 text="Florian Krause <siebenhundertzehn@gmail.com>",
                                 font=("Arial", "8"))
        self.aboutlabel4.pack()
        self.aboutlabel5 = Label(self.aboutdialogue,
                                 text="Oliver Lindemann <lindemann09@gmail.com>",
                                 font=("Arial", "8"))
        self.aboutlabel5.pack()

    def help(self):
        """Show the help dialogue window"""

        self.helpdialogue = Toplevel(self.master, width=200, height=300, padx=5,
                                     pady=5)
        self.helpdialogue.title("Help Contents")
        self.helpdialogue.transient(self.master)
        self.helpdialogue.grab_set()
        self.helpdialogue.bind("<Escape>", lambda e:
                               self.helpdialogue.destroy())
        self.helpdialogue.bind("<Return>", lambda e:
                               self.helpdialogue.destroy())

        self.helpframe = Frame(self.helpdialogue)
        self.helpframe.pack()
        self.helptextbox = Text(self.helpframe, highlightthickness=0, wrap=WORD)
        self.helptextbox.insert('1.0', self.documentation)
        self.helptextbox.pack(side=LEFT)
        self.helptextbox.focus_set()
        self.scrollbar = Scrollbar(self.helpframe, takefocus=False,
                                   command=self.helptextbox.yview)
        self.scrollbar.pack(side=RIGHT, fill=Y)
        self.helptextbox.config(state=DISABLED,
                                yscrollcommand=self.scrollbar.set)
        self.closebutton = Button(self.helpdialogue, text="Close",
                                  takefocus=FALSE,
                                  command=self.helpdialogue.destroy)
        self.closebutton.pack()

    def quit_gui(self):
        """Quit the application"""

        self.stop_recording()
        self.quit()
        self.master.destroy()


if __name__ == "__main__":

    # Create root window
    root = Tk()
    root.title("Response Force Controller")
    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    root.geometry("%dx%d+0+0" % (w, h))

    # Maximize on Windows
    if sys.platform == "win32":
        root.state("zoomed")

    # Start Application
    app = ResponseForceControllerGUI(root)
    app.mainloop()
